{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/sharedata/mdy/miniforge/envs/cuda128/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "import torch\n",
    "from typing import Tuple\n",
    "import triton\n",
    "import pandas as pd\n",
    "from fp8 import DeepLinear, quant_input, quant_weight\n",
    "from copy import deepcopy\n",
    "import os\n",
    "os.environ['TRITON_PRINT_AUTOTUNING']= '1'\n",
    "from tqdm import tqdm\n",
    "from transformers import Trainer"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 精度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "device = 'cuda'\n",
    "dtype = torch.bfloat16\n",
    "m, n, k = 4096 * 2, 8192, 8192\n",
    "x1 = torch.randn(m, k, dtype=dtype, device=device)\n",
    "x1.requires_grad_(True)\n",
    "x2 = deepcopy(x1)\n",
    "fc1 = torch.nn.Linear(k,n, bias=False, dtype=dtype, device=device)\n",
    "fc2 = DeepLinear(k,n, bias=False, dtype=dtype, device=device)\n",
    "fc2.weight.data.copy_(fc1.weight.data)\n",
    "# dy = torch.randn(m, n, dtype=dtype, device=device)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[-0.0786, -0.0291, -0.0059,  ..., -0.1719,  0.6562,  0.6992],\n",
      "        [-0.3613,  0.1260, -0.2295,  ...,  0.9102, -0.8164, -0.6523],\n",
      "        [-0.5781, -0.0552, -0.0029,  ...,  0.0583, -0.8516,  1.7109],\n",
      "        ...,\n",
      "        [-0.1719, -1.0391, -0.2305,  ..., -0.2520,  0.6719,  0.1377],\n",
      "        [ 0.5664, -0.1875,  0.5547,  ...,  0.3242,  0.8242, -0.1816],\n",
      "        [-0.9805,  0.1250, -0.1504,  ...,  0.4199, -0.2295, -0.3906]],\n",
      "       device='cuda:0', dtype=torch.bfloat16, grad_fn=<MmBackward0>)\n",
      "tensor([[-0.0583, -0.0248, -0.0031,  ..., -0.1729,  0.6953,  0.6641],\n",
      "        [-0.3789,  0.1196, -0.2217,  ...,  0.8867, -0.8203, -0.6406],\n",
      "        [-0.5742, -0.0649,  0.0026,  ...,  0.0444, -0.8320,  1.7188],\n",
      "        ...,\n",
      "        [-0.1689, -1.0703, -0.1953,  ..., -0.2656,  0.6836,  0.1396],\n",
      "        [ 0.5977, -0.2031,  0.5742,  ...,  0.3203,  0.8359, -0.1846],\n",
      "        [-0.9688,  0.1719, -0.0986,  ...,  0.4492, -0.2480, -0.4160]],\n",
      "       device='cuda:0', dtype=torch.bfloat16, grad_fn=<_DeepLinearBackward>)\n",
      "tensor([[-0.6172, -0.2344, -0.0918,  ..., -0.1523, -0.8438, -0.3027],\n",
      "        [-0.4453, -0.5039,  0.0408,  ...,  0.6914, -0.4141,  0.2871],\n",
      "        [ 0.0913, -0.1875, -0.0481,  ..., -0.8594,  0.4082,  0.6602],\n",
      "        ...,\n",
      "        [ 0.5820,  1.1641, -0.2480,  ...,  1.0000, -0.3496, -0.3691],\n",
      "        [-0.7930,  0.0547, -0.4277,  ..., -0.7461, -0.4648,  0.1338],\n",
      "        [-0.9102,  0.4844,  0.2676,  ..., -0.9844, -0.9688,  0.3496]],\n",
      "       device='cuda:0', dtype=torch.bfloat16)\n",
      "tensor([[-0.6602, -0.2490, -0.0933,  ..., -0.1699, -0.8594, -0.2910],\n",
      "        [-0.4688, -0.5117,  0.0408,  ...,  0.6875, -0.4336,  0.2695],\n",
      "        [ 0.0830, -0.1396, -0.0220,  ..., -0.8477,  0.4043,  0.6680],\n",
      "        ...,\n",
      "        [ 0.5430,  1.1562, -0.2344,  ...,  0.9961, -0.4004, -0.3633],\n",
      "        [-0.7656,  0.0459, -0.4121,  ..., -0.7656, -0.4570,  0.1338],\n",
      "        [-0.9102,  0.4961,  0.2598,  ..., -0.9844, -1.0078,  0.3184]],\n",
      "       device='cuda:0', dtype=torch.bfloat16)\n",
      "tensor([[ -18.6250,  -42.0000,   76.5000,  ...,  111.5000,  142.0000,\n",
      "         -100.5000],\n",
      "        [ 250.0000,  122.0000, -235.0000,  ...,   40.0000,   23.6250,\n",
      "          -28.2500],\n",
      "        [  18.8750,  195.0000,  100.5000,  ...,   23.0000,   -8.5625,\n",
      "           33.2500],\n",
      "        ...,\n",
      "        [ -75.0000, -120.5000,   -5.1875,  ...,   -7.1562,  188.0000,\n",
      "          -25.8750],\n",
      "        [   2.4844, -133.0000,  -72.5000,  ...,   -5.7812, -112.0000,\n",
      "           17.0000],\n",
      "        [  25.3750,   44.7500,   93.0000,  ...,   93.0000,  -24.7500,\n",
      "          -45.0000]], device='cuda:0', dtype=torch.bfloat16)\n",
      "tensor([[ -20.3750,  -46.2500,   75.5000,  ...,  113.5000,  143.0000,\n",
      "         -101.5000],\n",
      "        [ 251.0000,  121.5000, -238.0000,  ...,   43.7500,   29.8750,\n",
      "          -26.6250],\n",
      "        [  17.8750,  195.0000,  103.0000,  ...,   20.0000,   -6.6875,\n",
      "           29.6250],\n",
      "        ...,\n",
      "        [ -69.5000, -117.0000,   -0.9141,  ...,  -10.1250,  188.0000,\n",
      "          -31.2500],\n",
      "        [   4.1562, -131.0000,  -71.0000,  ...,   -4.8125, -109.5000,\n",
      "           23.7500],\n",
      "        [  28.2500,   42.7500,   87.0000,  ...,   93.5000,  -24.2500,\n",
      "          -46.0000]], device='cuda:0', dtype=torch.bfloat16)\n"
     ]
    }
   ],
   "source": [
    "y1 = fc1(x1)\n",
    "y2 = fc2(x2)\n",
    "dy = torch.randn_like(y1)\n",
    "y1.backward(dy)\n",
    "y2.backward(dy)\n",
    "print(y1)\n",
    "print(y2)\n",
    "print(x1.grad)\n",
    "print(x2.grad)\n",
    "print(fc1.weight.grad)\n",
    "print(fc2.weight.grad)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4.789308547973633\n",
      "3.1469065845012665\n"
     ]
    }
   ],
   "source": [
    "print(triton.testing.do_bench(lambda: fc1(x1).backward(dy)))\n",
    "# 可以把weight cache起来，第一个micro_bs去更新fp8_weight，可以去megatron_scrips中查看\n",
    "print(triton.testing.do_bench(lambda: fc2(x2).backward(dy)) - triton.testing.do_bench(lambda: quant_weight(fc2.weight.data, only_forward=False)))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.624271035194397\n",
      "1.1004228591918945\n",
      "3.3067338466644287\n",
      "2.1649904251098633\n"
     ]
    }
   ],
   "source": [
    "print(triton.testing.do_bench(lambda: fc1(x1)))\n",
    "print(triton.testing.do_bench(lambda: fc2(x2)))\n",
    "y1 = fc1(x1)\n",
    "y2 = fc2(x2)\n",
    "dy = torch.randn_like(y1)\n",
    "print(triton.testing.do_bench(lambda: y1.backward(dy, retain_graph=True)))\n",
    "print(triton.testing.do_bench(lambda: y2.backward(dy, retain_graph=True)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Forward"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjIAAAGwCAYAAACzXI8XAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAAWDVJREFUeJzt3Xt8j/X/x/HHZ+cNm/NYhjkfhxI5JKJUIh0UmUMpETnlWF86SiR0kKKSzKFSDiFyVs7nQ85nYchhM5udPtfvj+vnwwqt2XZ9Ds/77bbbd9dhn72ufXy3Z9f1fr9fNsMwDERERERckJfVBYiIiIhkloKMiIiIuCwFGREREXFZCjIiIiLishRkRERExGUpyIiIiIjLUpARERERl+VjdQHZzW63c/LkSfLkyYPNZrO6HBEREckAwzC4dOkSYWFheHnd/L6L2weZkydPEh4ebnUZIiIikgnHjx+nWLFiNz3u9kEmT548gPmDCA4OtrgaERERyYi4uDjCw8Mdf8dvxu2DzNXHScHBwQoyIiIiLubfhoVosK+IiIi4LAUZERERcVkKMiIiIuKy3H6MTEalpaWRkpJidRkux8/P75bT4kRERLKTxwcZwzCIiYnh4sWLVpfikry8vIiIiMDPz8/qUkRExAN5fJC5GmIKFy5MUFCQFs37D64uNnjq1CmKFy+un52IiOQ4jw4yaWlpjhBToEABq8txSYUKFeLkyZOkpqbi6+trdTkiIuJhPHpww9UxMUFBQRZX4rquPlJKS0uzuBIREfFEHh1krtIjkczTz05ERKykICMiIiIuS0FGREREXJaCjItq2LAhvXr1uunxhIQEnnzySYKDg7HZbJpeLiIibklBxk1NmjSJ3377jdWrV3Pq1ClCQkL46aefePDBBylQoAA2m42tW7fe8GvXrFnD/fffT65cuQgODqZBgwYkJibm7AWIiIjTS0yERYusrUFBxk0dPHiQihUrUqVKFYoUKYLNZuPy5cvUr1+f4cOH3/Tr1qxZw0MPPcSDDz7I+vXr2bBhA927d9fqvSIiko5hwAsvwIMPwqhR1tXh0evI/J1hQEKCNd87KAj+6wSg1NRUunfvzuTJk/H19aVr1668/fbbNGrUiBUrVgDmrKL77ruP5cuX065dOwCOHDly09fs3bs3PXr0YODAgY595cuX/8/XIyIi7m3ECJg6FXx84M47ratDQeY6CQmQO7c13zs+HnLl+m9fM2nSJDp16sT69evZuHEjnTt3pnjx4vz0008MHDiQnTt38tNPP2W4fcCZM2dYt24dbdu2pW7duhw8eJAKFSowdOhQ6tevn4mrEhERdzRvHgwaZH7+0UfQsKF1tSjIuLDw8HBGjx6NzWajfPny7Nixg9GjR/Piiy8SFBSEn58fRYoUyfDrHTp0CIA333yTkSNHUr16db799lsaN27Mzp07KVu2bHZdioiIuIjdu+HZZ82nGJ07Q9eu1tajIHOdoCDzzohV3/u/uueee9ItSFenTh0+/PDDTK+ya7fbAXjppZd47rnnAKhRowZLlizh66+/ZtiwYZl6XRERcQ8XLkCLFhAXB/feC5988t+HRWQ1BZnr2Gz//fGOOylatCgAlSpVSre/YsWKHDt2zIqSRETESaSmwjPPwIEDULw4zJgBGRy5kK00FcWFrVu3Lt322rVrKVu2LN7e3pl6vZIlSxIWFsbevXvT7d+3bx8lSpTIdJ0iIuL6+vc3p1oHBcHs2VC4sNUVmSwNMitXrqR58+aEhYVhs9mYNWuW41hKSgoDBgygatWq5MqVi7CwMNq3b8/JkyetK9jJHDt2jD59+rB3716mTZvGJ598Qs+ePW96/vnz59m6dSu7du0CYO/evWzdupWYmBjAnOHUr18/Pv74Y2bMmMGBAwcYPHgwe/bsoVOnTjlyTSIi4ny++QZGjzY/nzQJqle3spr0LH20dPnyZapVq8bzzz/PE088ke5YQkICmzdvZvDgwVSrVo0LFy7Qs2dPWrRowcaNGy2q2Lm0b9+exMREatWqhbe3Nz179qRz5843PX/OnDmOsS8ArVu3BuCNN97gzTffBKBXr15cuXKF3r17c/78eapVq8aiRYsoXbp0tl6LiIg4pzVr4KWXzM8HD4annrK2nr+zGYZhWF0EmHcDZs6cScuWLW96zoYNG6hVqxZHjx6lePHiGXrduLg4QkJCiI2NJTg4ON2xK1eucPjwYSIiIggICLid8j2WfoYiIu7rxAmoWRNiYqBlS/jxR8ip9VFv9ff7ei412Dc2NhabzUbevHlvek5SUhJJSUmO7bi4uByoTERExL0kJprhJSYGqlSByZNzLsT8F05Y0o1duXKFAQMG0KZNm1sms2HDhhESEuL4CA8Pz8EqRUREXN/V9gMbN0KBAjBnjnULxv4blwgyKSkpPP300xiGwbhx42557qBBg4iNjXV8HD9+PIeqFBERcQ8ffGC2H/D2hh9+gIgIqyu6Oad/tHQ1xBw9epSlS5fe8m4MgL+/P/7+/jlUnYiIiHuZNw+uttv7+GNo1Mjaev6NUweZqyFm//79LFu2jAIFClhdkoiIiNtytvYDGWFpkImPj+fAgQOO7cOHD7N161by589P0aJFeeqpp9i8eTNz584lLS3Nsd5J/vz5M9wIUURERP6dM7YfyAhLg8zGjRtpdN09qz59+gDQoUMH3nzzTebMmQNA9b+tvLNs2TIaWtlqU0RExI2kpkLr1s7XfiAjLA0yDRs25FbL2DjJEjciIiJubcAA+PVX52s/kBEuMWtJ/skwDDp37kz+/Pmx2Wxs3brV6pJERMQFTZoEo0aZn3/zjXO1H8gIBRkXtWDBAr755hvmzp3LqVOnqFKlyr9+TXx8PN27d6dYsWIEBgZSqVIlPv/88xyoVkREnNHateagXjDbD7RqZW09meHUs5bk5g4ePEjRokWpW7duhr+mT58+LF26lOjoaEqWLMmvv/7Kyy+/TFhYGC1atMjGakVExNmcOAGPPw7JyeYKvv/fcs/l6I6MC+rYsSOvvPIKx44dw2azUbJkSRo2bEj37t3p3r07ISEhFCxYkMGDB6cbZ7R69Wo6dOhAw4YNKVmyJJ07d6ZatWqsX7/ewqsREZGc9vf2A99+65ztBzJCd2SuYxgGCSkJlnzvIN8gbBmc5/bRRx9RunRpxo8fz4YNG/D29qZVq1ZMmjSJTp06sX79ejZu3Ejnzp0pXrw4L774IgB169Zlzpw5PP/884SFhbF8+XL27dvH6Ku92UVExO1d334gf35zcG+ePFZXlXkKMtdJSEkg9zBrmknED4onl1+uDJ0bEhJCnjx58Pb2pkiRIo794eHhjB49GpvNRvny5dmxYwejR492BJlPPvmEzp07U6xYMXx8fPDy8mLChAk0aNAgW65JREScz/XtB2bMgFKlrK7o9rjojSS5kXvuuSfdXZ06deqwf/9+0tLSADPIrF27ljlz5rBp0yY+/PBDunXrxuLFi60qWUREctD8+dfaD3z0kfO3H8gI3ZG5TpBvEPGD4i373tkpMTGR1157jZkzZ9KsWTMAIiMj2bp1KyNHjqRJkybZ+v1FRMRau3dDmzbX2g+8/LLVFWUNBZnr2Gy2DD/ecUbr1q1Lt7127VrKli2Lt7c3KSkppKSk4PW30Vze3t7Y7facLFNERHLYhQvw2GOu134gIxRk3MixY8fo06cPL730Eps3b+aTTz7hww8/BCA4OJj77ruPfv36ERgYSIkSJVixYgXffvsto66uhCQiIm7navuB/ftdr/1ARijIuJH27duTmJhIrVq18Pb2pmfPnnS+utIRMH36dAYNGkTbtm05f/48JUqUYOjQoXTp0sXCqkVEJDu5cvuBjLAZbt7QKC4ujpCQEGJjYwkODk537MqVKxw+fJiIiAgCAgIsqjBrNGzYkOrVqzNmzJgc/b7u9DMUEXE3kyZBx47m599/71or997q7/f1NGtJRETEDblD+4GMUJARERFxM+7SfiAjNEbGTSxfvtzqEkRExAlc336gcmXXbj+QEW58aSIiIp7FMODFF6+1H5gzx7XbD2SEggzg5uOds5V+diIizuODD2DKFLP9wA8/uH77gYzw6CDj6+sLQEKCNY0i3UFycjJgLqwnIiLW+Xv7gfvvt7aenOLRY2S8vb3JmzcvZ86cASAoKOMdqAXsdjtnz54lKCgIHx+P/qckImKpPXuutR948UX3aT+QER7/1+dq9+irYUb+Gy8vL4oXL64AKCJikQsXoEULs/1A/frw6afu034gIzw+yNhsNooWLUrhwoVJSUmxuhyX4+fn94/+TSIikjNSU807MVfbD/z4o3u1H8gIjw8yV3l7e2uch4iIuJQBA2DhQggMdM/2Axmh/5QWERFxQZMmwdWev5MmQfXqlpZjGQUZERERF3N9+4H//c992w9khIKMiIiIC7m+/cBjj8Fbb1ldkbUUZERERFxEYqIZYq62H5g82b3bD2SEh1++iIiIa7i6RsyGDZ7TfiAjFGRERERcwMiRntd+ICMUZERERJzc/PnmVGuAMWM8p/1ARijIiIiIOLG/tx/o1s3qipyLgoyIiIiTunDBnJnkqe0HMkJBRkRExAmlpZl3Yvbtg/Bwz2w/kBEKMiIiIk7o+vYDc+Z4ZvuBjFCQERERcTLffgsffmh+/s03ntt+ICMUZERERJzI2rXmoF4w2w88/bS19Tg7BRkREREnofYD/52CjIiIiBNQ+4HM0Y9IRETEYoZhdrNW+4H/TkFGRETEYiNHQnS02g9khoKMiIiIhdR+4PYoyIiIiFjk+vYDL7yg9gOZoSAjIiJigb+3Hxg7Vu0HMkNBRkREJIep/UDWUZARERHJYde3H5g9W+0HboelQWblypU0b96csLAwbDYbs2bNSnfcMAyGDBlC0aJFCQwMpEmTJuzfv9+aYkVERLLA39sP1KhhaTkuz9Igc/nyZapVq8bYsWNveHzEiBF8/PHHfP7556xbt45cuXLRtGlTrly5ksOVioiI3L5168z1YgBef13tB7KCzTAMw+oiAGw2GzNnzqRly5aAeTcmLCyMV199lb59+wIQGxtLaGgo33zzDa1bt87Q68bFxRESEkJsbCzBwcHZVb6IiMgtnTgBd98Np06Zg3x/+kkr995KRv9+O+2P8PDhw8TExNCkSRPHvpCQEGrXrs2aNWtu+nVJSUnExcWl+xAREbHS1fYDp06p/UBWc9ofY0xMDAChoaHp9oeGhjqO3ciwYcMICQlxfISHh2drnSIiIrfy9/YDs2er/UBWctogk1mDBg0iNjbW8XH8+HGrSxIREQ/29/YDpUtbXZF7cdogU6RIEQBOnz6dbv/p06cdx27E39+f4ODgdB8iIiJW+OWXa+0HRo9W+4Hs4LRBJiIigiJFirBkyRLHvri4ONatW0edOnUsrExEROTf7dkDrVtfaz/QvbvVFbknHyu/eXx8PAcOHHBsHz58mK1bt5I/f36KFy9Or169ePfddylbtiwREREMHjyYsLAwx8wmERERZ3Tx4rX2A/Xqqf1AdrI0yGzcuJFGjRo5tvv06QNAhw4d+Oabb+jfvz+XL1+mc+fOXLx4kfr167NgwQICAgKsKllEROSW0tLMOzFqP5AznGYdmeyidWRERCQn9e1rrtwbGAirVmnl3sxy+XVkREREXI3aD+Q8BRkREZEsoPYD1lCQERERuU0nT5or9yYlQYsW8PbbVlfkORRkREREbkNiIrRsqfYDVtGPWkREJJNu1H5A80pyloKMiIhIJn344bX2A99/r/YDVlCQERERyYTp06F/f/Pz0aOhcWNr6/FUCjIiIiL/0fz50K6d+Wipa1e1H7CSgoyIiMh/sHIlPPkkpKZCmzbw6adqP2AlBRkREZEM2rwZmjeHK1egWTOYNEkzlKymH7+IiEgG7NkDTZuajSAbNIAffgBfX6urEgUZERGRf3H0KDzwAPz1F9x1F/z8s9lLSaynICMiInILp09Dkybw559QoQIsWKC1YpyJgoyIiMhNXLhgPk46cABKlIBFi6BgQaurkuspyIiIiNzA5cvmgN5t2yA0FBYvhmLFrK5K/k5BRkRE5G+SkuCJJ2DNGsibF379FcqUsboquREFGRERkeukpUFUlBlegoLMxe8iI62uSm5GQUZEROT/XW0COWMG+PnBrFlQp47VVcmtKMiIiIhghpi+feHrr81F7qZNM6dci3NTkBEREQGGDoVRo8zPv/zSHCMjzk9BRkREPN6nn8Lgwebno0fDc89ZW49knIKMiIh4tMmT4ZVXzM+HDIFevSwtR/4jBRkREfFYs2dfu/vyyivw5puWliOZoCAjIiIeaelSeOYZc7p1+/YwZgzYbFZXJf+VgoyIiHic9euhRQtz4buWLeGrr8yZSuJ69LaJiIhH2bkTHn7YbEFw//3mNGsfH6urksxSkBEREY9x6BA8+CCcPw+1a5sL3gUEWF2V3A4FGRER8QgnT5oL3J06BVWqmK0H8uSxuiq5XQoyIiLi9s6dM+/EHDoEpUqZfZTy57e6KskKCjIiIuLWLl2CRx6BP/6AsDBYvBiKFrW6KskqCjIiIuK2rlwxZyWtX2/egfn1V4iIsLoqyUoKMiIi4pZSU6F1a3O9mNy5YcECqFzZ6qokqynIiIiI27Hb4fnnzZV7/f1hzhy4+26rq5LsoCAjIiJuxTDMfkmTJ4O3N/zwAzRqZHVVkl0UZERExK288QZ88on5+TffQPPmlpYj2UxBRkRE3Mbo0fDOO+bnn34KUVHW1iPZT0FGRETcwtdfQ58+5ufvvgvdullbj+QMBRkREXF5P/4IL75ofv7qq/Daa9bWIzlHQUZERFzar79CmzbmTKVOneCDD8Bms7oqySkKMiIi4rJWr4bHH4eUFGjVCr74QiHG0yjIiIiIS9q2DZo1g4QEaNoUoqPN6dbiWRRkRETE5ezfbzaBvHgR6tUzx8j4+VldlVhBQUZERFzKn39CkyZw5gxUrw5z50KuXFZXJVZRkBEREZdx9iw88AAcOwZly5r9k/LmtboqsZKCjIiIuITYWHjoIdizB4oVg8WLITTU6qrEak4dZNLS0hg8eDAREREEBgZSunRp3nnnHQzDsLo0ERHJQYmJ0KIFbN4MhQrBokVQvLjVVYkz8LG6gFsZPnw448aNY9KkSVSuXJmNGzfy3HPPERISQo8ePawuT0REcsDVqdUrV0JwMCxcCBUqWF2VOAunDjKrV6/mscceo1mzZgCULFmSadOmsX79+pt+TVJSEklJSY7tuLi4bK9TRESyR1oatG8P8+ZBQIA5sLdGDaurEmfi1I+W6taty5IlS9i3bx8A27Zt4/fff+fhhx++6dcMGzaMkJAQx0d4eHhOlSsiIlnIMMx+SdOng48P/PQT3Huv1VWJs7EZTjzgxG6389prrzFixAi8vb1JS0tj6NChDBo06KZfc6M7MuHh4cTGxhIcHJwTZYuISBYYNAjef99cqXfaNHjmGasrkpwUFxdHSEjIv/79dupHS99//z1Tpkxh6tSpVK5cma1bt9KrVy/CwsLo0KHDDb/G398ff3//HK5URESy0vDhZogBs+2AQozcjFMHmX79+jFw4EBat24NQNWqVTl69CjDhg27aZARERHX9sUXMHCg+fnw4de6WovciFOPkUlISMDLK32J3t7e2O12iyoSEZHsNH06dO1qfj5oEPTvb2094vyc+o5M8+bNGTp0KMWLF6dy5cps2bKFUaNG8fzzz1tdmoiIZLH586FdO3OQb9euMHSo1RWJK3Dqwb6XLl1i8ODBzJw5kzNnzhAWFkabNm0YMmQIfhnsDpbRwUIiImKdlSvNDtZXrkCbNmYnay+nfmYg2S2jf7+dOshkBQUZERHntnkzNGoEcXHQrBnMnAm+vlZXJVbL6N9v5V0REbHMnj3mnZi4OLjvPvjhB4UYV7Lnrz08M+MZLidftqwGpx4jIyIi7uvoUbOT9V9/wV13wZw5EBhodVWSEYZh8OXmL+m5oCeJqYmE5Q5j9EOjLalFQUZERHLc6dPQpAn8+SdUrAgLFph9lMT5nU88z4s/v8hPu38C4IFSD9C/nnXTyxRkREQkR128aD5OOnAASpSAX3+FggWtrkoyYsWRFUTNjOLPuD/x9fJlWONh9K7TGy+bdSNVFGRERCTHXL5sDujdtg1CQ2HxYihWzOqq5N+kpKXw1oq3eO+39zAwKJu/LNOenMZdYXdZXZqCjIiI5IykJHjiCVi9GvLmNe/ElCljdVXybw5dOMSzPz7LuhPrAOhUoxNjHhpDbr/cFldmUpAREZFsl5YGUVFmeAkKMhe/i4y0uir5N1O2T6HrvK5cSr5E3oC8jH90PK0qt7K6rHQUZEREJFsZBrz0EsyYAX5+MGsW1KljdVVyK3FJcXSb343o7dEA1C9enylPTKF4SHGLK/snBRkREck2hgF9+8JXX5kr9U6bZk65Fue19s+1PPvjsxy+eBhvmzdv3PcGg+4dhI+Xc0YG56xKRETcwtChMGqU+fmXX5pjZMQ5pdnTGL5qOEOWDSHNSKNk3pJMeWIKdcPrWl3aLSnIiIhItvj0Uxg82Px89Gh47jlr65GbOx57nHYz27Hi6AoAWldpzefNPickIMTiyv6dgoyIiGS56Gh45RXz8yFDoFcvS8uRW/hp90+8MOcFLly5QG6/3Ix9ZCztItths9msLi1DFGRERCRLzZ4NHTuan/foAW++aWU1cjOXky/TZ2Efxm8eD0DNsJpMe3IaZfK71px4BRkREckyy5bBM8+Y0607dDAfKbnIf9h7lK0xW2nzYxv2/LUHGzYG1BvAW43ews/bz+rS/jMFGRERyRLr10OLFubCdy1bmoN7vaxbuV5uwG7Y+WjtRwxcMpDktGTC8oTxbctvaVyqsdWlZZqCjIiI3LadO+HhhyE+Hho3NqdZ++gvjFM5HX+ajrM7suDAAgAeK/8YX7b4koJBrt3oSv/MRETkthw6BA8+COfPQ+3a5oJ3AQFWVyXX+2X/L3Sc3ZEzl88Q4BPA6Kajeemul1xmQO+tKMiIiEimnTplLnB36hRUqWK2HsjtHC14BEhKTWLA4gF8tO4jAKoWrsq0J6dRuXBliyvLOgoyIiKSKefOmXdiDh2CUqXMPkr581tdlVy1++xu2vzYhm2ntwHQo1YPhj8wnAAf97pdpiAjIiL/2bFj8NBDsHs3hIXB4sVQtKjVVQmAYRiM3zSe3gt7k5iaSKGgQkx8bCLNyjWzurRsoSAjIiL/yY4dZog5eRKKFTPvxEREWF2VAJxLOMeLP7/IzD0zAXiw9IN889g3FM3jvilTQUZERDJsxQp47DGIjYVKlWDBAggPt7oqAVh2eBntZrbjxKUT+Hr58n6T9+l1Ty+8bO49B15BRkREMuSHHyAqCpKT4d57zRV88+WzuipJSUvhjeVv8P7v72NgUL5AeaY9OY0aRWtYXVqOUJAREZF/9ckn0LMnGAY8/jhMmQKBgVZXJQfPH+TZn55l/Yn1ALxQ4wXGPDSGXH65LK4s5yjIiIjITRkGvPYavP++uf3yy/Dxx+DtbW1dApO3Tebl+S8TnxxP3oC8TGg+gacqPWV1WTlOQUZERG4oJQVeeAG+/dbcfvddM9S4wRpqLi32Sizd5ndjyo4pANxb/F6in4imeEhxiyuzhoKMiIj8Q3w8PPUULFxo3n2ZMAGee87qqmTtn2t59sdnOXzxMN42b95s+CaD6g/C28tzb5EpyIiISDpnzkCzZrBxIwQFwfffm9tinTR7GsN+H8aby98kzUijZN6STH1iKnXC61hdmuUUZERExOHgQWja1PzfggVh3jyoVcvqqjzb8djjRM2MYuXRlQC0qdKGcc3GERIQYnFlzkFBRkREANi0CR55xLwjU7Kk+VipXDmrq/JsP+76kRd+foGLVy6S2y83nz3yGVGRUW7R7DGrKMiIiAgLF8KTT8Lly1Cjhtn8sUgRq6vyXJeTL9N7YW8mbJ4AQK07ajH1iamUzl/a4sqcj4KMiIiHmzwZnn8eUlOhSRP48UcIDra6Ks+15dQW2vzYhr3n9mLDxsD6A3mr4Vv4evtaXZpTUpAREfFQhgEffAADBpjbzz4LEyeCn5+1dXkqu2FnzNoxDFw8kBR7CnfkuYPJj0+mUUQjq0tzagoyIiIeyG6H3r3Nxe0AXn0VRowAL/duy+O0YuJj6DirIwsPLgSgZYWWfNn8SwoEFbC4MueXqX+ykyZNYt68eY7t/v37kzdvXurWrcvRo0ezrDgREcl6V65A69bXQsyoUTBypEKMVebvn0/kuEgWHlxIoE8gnzf7nJ+e/kkhJoMy9c/2vffeI/D/m2ysWbOGsWPHMmLECAoWLEjv3r2ztEAREck6sbHw8MNmA0hfX5g2zbwzIznvSuoVev7Sk2ZTm3E24SyRoZFs7LyRl2q+pFlJ/0GmHi0dP36cMmXKADBr1iyefPJJOnfuTL169WjYsGFW1iciIlnkxAkzxOzYAXnywMyZ0Lix1VV5pl1nd9F6Rmt2nNkBQM/aPXm/yfsE+ARYXJnrydQdmdy5c3Pu3DkAfv31Vx544AEAAgICSExMzLrqREQkS+zeDXXrmiGmSBFYuVIhxgqGYfD5xs+5a/xd7Dizg0JBhZj37DzGPDRGISaTMnVH5oEHHuCFF16gRo0a7Nu3j0ceeQSAP/74gxIlSmRpgSIicntWr4ZHH4ULF8wF7hYsgIgIq6vyPH8l/MULc15g9t7ZADQt3ZRvWn5DkdxasOd2ZOqOzNixY6lTpw5nz57lxx9/pEABc0DSpk2bePbZZ7O0QBERybw5c8w7LxcuQO3asGqVQowVlh5eSrXPqzF772x8vXwZ9eAo5redrxCTBWyGYRiZ+cIrV66wfft2zpw5g91uT3esRYsWWVJcVoiLiyMkJITY2FiCtcKTiHiQ8eOha1dzqnWzZvDdd5Arl9VVeZaUtBSGLBvC8FXDMTAoX6A8056cRo2iNawuzell9O93ph4tLViwgPbt23Pu3Dn+noNsNhtpaWmZeVkREckChgFvvWV+gLlq7xdfgI9WDstRB84f4Nkfn2XDyQ0AdL6zM6OajiKXn9JkVsrUo6VXXnmFVq1acfLkSex2e7oPhRgREeukpsJLL10LMYMHw5dfKsTkJMMwmLR1EjW+qMGGkxvIF5CPGa1m8EXzLxRiskGm/mmfPn2aPn36EBoamtX1iIhIJiUkmAvd/fyzubjd2LHQpYvVVXmW2CuxdJ3XlWk7pwFwX4n7mPz4ZMJDwi2uzH1l6o7MU089xfLly7O4lBs7ceIEUVFRFChQgMDAQKpWrcrGjRtz5HuLiLiKc+fMho8//wwBAWbjR4WYnLX6+Gqqf1GdaTun4W3z5t1G77Kk/RKFmGyWqTsyn376Ka1ateK3336jatWq+Pqm78jZo0ePLCnuwoUL1KtXj0aNGvHLL79QqFAh9u/fT758+bLk9UVE3MGRI/DQQ7B3L+TLZ4aZevWsrspzpNnTGPrbUN5e8TZpRhoReSOY+uRU7il2j9WleYRMBZlp06bx66+/EhAQwPLly9MtpWyz2bIsyAwfPpzw8HAmTpzo2BfxL/MGk5KSSEpKcmzHxcVlSS0iIs5o2zZztd5TpyA83FwjplIlq6vyHMdijxH1UxS/HfsNgLZV2/JZs88I9tcs2ZySqUdLr7/+Om+99RaxsbEcOXKEw4cPOz4OHTqUZcXNmTOHmjVr0qpVKwoXLkyNGjWYMGHCLb9m2LBhhISEOD7Cw3VLT0Tc07Jl0KCBGWKqVDEXvlOIyRmGYTB522Qix0Xy27HfyO2Xm8mPTyb6iWiFmByWqXVk8ufPz4YNGyhdunR21OQQEGAu19ynTx9atWrFhg0b6NmzJ59//jkdOnS44dfc6I5MeHi41pEREbfy/ffQrh0kJ5thZvZsyJvX6qo8w5nLZ+gytwsz98wEoPYdtZnyxBRK58/ev4meJqPryGQqyPTu3ZtChQrx2muv3VaR/8bPz4+aNWuyevVqx74ePXqwYcMG1qxZk6HX0IJ4IuJuPvrI7FhtGPDUUzB5sjnAV7LfzN0zeWnuS5xNOIuvly9v3PcGA+oPwMdL89uzWrYuiJeWlsaIESNYuHAhkZGR/xjsO2rUqMy87D8ULVqUSn+7T1qxYkV+/PHHLHl9ERFXYrfDwIHwwQfmdvfuMGYMeHtbWpZHuJB4gR4LehC9PRqAqoWr8u3j31K9SHVrC5PMBZkdO3ZQo4a5vPLOnTvTHbt+4O/tqlevHnv37k23b9++fWpMKSIeJzkZOnWCaPPvKMOGwYABkIW/cuUmFh5YSKc5nThx6QReNi8G1BvAG/e9gb+Pv9WlCZkMMsuWLcvqOm6od+/e1K1bl/fee4+nn36a9evXM378eMaPH58j319ExBlcumQ+Qvr1V/Puy1dfwU2GCUoWik+Op++vffli0xcAlM1flkktJ1EnvI7Flcn1Mt00MqfMnTuXQYMGsX//fiIiIujTpw8vvvhihr9eY2RExJWdPg2PPAKbN5sNH2fMMNeMkez129Hf6Di7I4cumDNxe9TqwbAmwwjyDbK4Ms+RrYN9XYmCjIi4qv37zdBy6BAUKgTz5sHdd1tdlXu7knqF/y39H6PWjMLAoHhIcSY+NpH7I+63ujSPk62DfUVEJHtt2ADNmsHZs1CqFCxcCGXKWF2Ve9t4ciPtZ7Zn91+7AXi++vOMajqKkIAQiyuTW1GQERFxMr/8Yo6JSUiAO++E+fNBPXqzT0paCu+ufJehvw0lzUgjNFcoE5pPoHn55laXJhmgICMi4kQmTTJnJ6WlwYMPmmNi8uSxuir3tfPMTtrPbM+WmC0APF35aT575DMKBBWwuDLJqEy1KBARkaxlGOaU6o4dzRATFWU2f1SIyR5p9jQ+WPUBd42/iy0xW8gfmJ/pT07nu6e+U4hxMbojIyJisbQ06NULPv3U3O7f3ww1XvpPzWxx4PwBOs7qyKrjqwBoVrYZE5pPoGieohZXJpmhICMiYqErV8yeSTNmmIvbjR4NPXtaXZV7MgyDcRvH0W9RPxJSEsjjl4cxD43huerPZelirpKzFGRERCxy8SI89hisXAl+fmbPpKeftroq93Q89jid5nRi0aFFADQq2YivH/uaknlLWluY3DYFGRERC/z5Jzz8MOzcCcHBMGsWNGpkdVXuxzAMJm+fTI9fehCbFEuATwDDmwyne63ueNn07M4dKMiIiOSwXbvMhe6OH4eiRWHBAoiMtLoq93M6/jRd5nVh1p5ZANS+ozaTWk6ifMHy1hYmWUpBRkQkB/3+O7RoARcuQPny5kJ36oOb9X7c9SNd5nXhr4S/8PXy5a2Gb9GvXj98vPRnz93oHRURySGzZkGbNuYA3zp1zOnVBTTTN0tdSLxA91+6M3XHVAAiQyP5tuW3VCtSzeLKJLsoyIiI5IDPP4du3cBuh+bNYfp0CFL/wSy14MACOs3pxMlLJ/GyeTGo/iCG3DcEP28/q0uTbKQgIyKSjQwDhgyBd981t198ET77DHz02zfLXEq6RN9f+zJ+83gAyhUox7ctv6V2sdoWVyY5Qf9XEhHJJqmp8NJL8PXX5vabb5qhRkuWZJ2VR1fScVZHDl88DEDP2j15r/F7BPnqdpenUJAREckGly/DM8/AvHnmCr3jxkHnzlZX5T4SUxJ5fenrjFk7BgODEiElmPjYRBpFaA67p1GQERHJYn/9BY8+CuvWQUAAfPedOVNJssaGExtoP6s9e/7aA0CnGp0Y1XQUwf7BFlcmVlCQERHJQkeOQNOmsG8f5M9vzkyqW9fqqtxDcloy7658l/d+e480I40iuYvwZfMvaVaumdWliYUUZEREssjWreZqvTExULy4udBdxYpWV+Uedp7ZSfuZ7dkSswWA1lVa8+nDn6pTtSjIiIhkhSVL4PHH4dIlc5XeX36BsDCrq3J9afY0PlzzIYOXDSY5LZkCgQX4rNlnPF1ZTanEpCAjInKbpk+H9u0hJQUaNjQXvgsJsboq17f/3H46zOrAmj/XAPBouUeZ0HwCRXIXsbgycSbqmCUichtGjTJX601JMTtXL1igEHO77IadsevHUv2L6qz5cw15/PLwdYuvmdN6jkKM/IPuyIiIZEJKCvTvD2PGmNs9esDo0eZUa8m8Y7HH6DSnE4sPLQbg/oj7+brF15TIq4ZUcmMKMiIi/9Hx49C6NaxebW4PHw79+mmhu9thGAaTtk2i54KexCXFEegTyIgHRvDy3S/jZVM6lJtTkBER+Q9++QXatYNz58xHSF9/DU88YXVVri0mPoaX5r7EnL1zALin2D1MajmJcgXKWVyZuAIFGRGRDEhNhcGD4f33ze277oLvv4dSpayty9XN2DWDLnO7cC7xHL5evrzd6G361e2Ht5e31aWJi1CQERH5FydOmAN6f/vN3O7eHUaOBH9/a+tyZecTz9N9fnem7ZwGQLXQanz7+LdEhkZaXJm4GgUZEZFbWLgQoqLMtgN58sBXX0GrVlZX5drm75/PC3Ne4FT8Kbxt3gyqP4jB9w3Gz9vP6tLEBSnIiIjcQGqq2a36vffAMKB6dfjhByhTxurKXNelpEv0WdiHL7d8CUD5AuX59vFvqXVHLYsrE1emICMi8jenTpmPklasMLe7djXXiwkIsLYuV7b8yHKem/0cRy4eAaBX7V681/g9An0DrS1MXJ6CjIjIdRYvhrZt4cwZyJ0bJkwwp1pL5iSmJPLaktcYs24MACXzlmTiYxNpWLKhpXWJ+1CQEREB0tLg7bfhnXfMR0mRkeajpHKaAZxp60+sp/3M9uw9txeAF+98kQ8f/JA8/nksrkzciYKMiHi8mBjzLszSpeZ2587mir2BeuqRKclpybyz4h2G/T6MNCONormL8mWLL3mk7CNWlyZuSEFGRDza0qXw7LNw+jTkygVffGGGGsmc7ae3035me7ad3gZAmypt+PSRT8kfmN/iysRdKciIiEdKS4OhQ+Gtt8BuhypVzEdJFSpYXZlrSrWnMnL1SIYsG0KKPYUCgQUY12wcrSprrrpkLwUZEfE4Z86Yd10Wm30J6dQJPv4YgoKsrctV7Tu3jw6zOrD2z7UAtCjfgvGPjic0d6jFlYknUJAREY+yYoU5tfrUKTO4jBsH7dtbXZVrsht2xq4fy4DFA0hMTSTYP5iPH/qY9tXaY1MHTckhCjIi4hHsdhg2DIYMMT+vVMl8lFSpktWVuaZlh5fRd1FfNp/aDEDjiMZ8/djXFA8pbnFl4mkUZETE7Z09a3asXrjQ3O7QAcaONQf3yn+z++xuBiwewM/7fgYgj18ehjUeRte7u+Jl87K4OvFECjIi4tZ++81c0O7kSXM69WefQceOVlflek7Hn+bN5W8yYfME0ow0vG3edKnZhSH3DaFwrsJWlyceTEFGRNyS3Q4jRsD//mfOUKpQwXyUVKWK1ZW5loSUBEatGcXwVcOJT44H4LHyjzG8yXDKFyxvcXUiCjIi4ob++st8fDR/vrkdFWUO6s2d29q6XEmaPY3J2yfzv6X/48SlEwDcHXY3Ix8cSYMSDSyuTuQaBRkRcSurVpmPkv7802zy+Omn8PzzoEk0Gbfo4CL6LurL9tPbASgRUoJhjYfxTJVnNA5GnI6CjIi4BbsdPvwQBg0yHyWVK2c+SoqMtLoy17Hj9A76L+7PggMLAAjxD+F/Df5H91rdCfBR629xTgoyIuLyzp83HyXNnWtut2ljthrIo96EGXLy0kmGLBvCxK0TsRt2fL186XZ3N/7X4H8UCCpgdXkit+RS9wjff/99bDYbvXr1sroUEXESa9dCjRpmiPH3NwPMlCkKMRkRnxzPG8veoOwnZflqy1fYDTtPVXqKXd12Mfqh0Qox4hJc5o7Mhg0b+OKLL4jUfWIRAQwDRo+GAQMgNRXKlDEfJVWvbnVlzi/VnsrELRMZsnwIMfExANQpVoeRD46kbnhdi6sT+W9c4o5MfHw8bdu2ZcKECeTLl8/qckTEYhcuwOOPw6uvmiHm6adh0yaFmH9jGAbz98+n2ufV6Dy3MzHxMZTOV5ofWv3AqudXKcSIS3KJINOtWzeaNWtGkyZN/vXcpKQk4uLi0n2IiPtYv958lDR7Nvj5mQvcTZ8OwcFWV+bctpzawgOTH6DZ1GbsOruL/IH5GdN0DLu67eKpSk+pN5K4LKd/tDR9+nQ2b97Mhg0bMnT+sGHDeOutt7K5KhHJaYZhdqju1w9SUqBUKfNR0p13Wl2Zczsee5z/Lfsfk7dNxsDAz9uPHrV68Nq9r5EvUHe4xfU5dZA5fvw4PXv2ZNGiRQQEZGzq36BBg+jTp49jOy4ujvDw8OwqUURywMWL0KkT/PSTuf3kk/DVVxASYmlZTi0uKY7hvw9n1NpRXEm9AkCbKm14r/F7lMxb0triRLKQzTAMw+oibmbWrFk8/vjjeHt7O/alpaVhs9nw8vIiKSkp3bEbiYuLIyQkhNjYWIJ171nE5WzcaI6BOXwYfH1h1Cjo1k0L3N1MSloKEzZP4M3lb3I24SwADUo0YOQDI7n7jrstrk4k4zL699up78g0btyYHTt2pNv33HPPUaFCBQYMGPCvIUZEXJdhmB2qX30VkpMhIgK+/x5q1rS6MudkGAZz9s5hwOIB7D23F4ByBcoxoskIWpRvoTEw4racOsjkyZOHKn/r8JYrVy4KFCjwj/0i4j5iY+HFF80xMGDOUPr6a8ib19KynNaGExvou6gvK4+uBKBgUEHeavgWL975Ir7evhZXJ5K9nDrIiIjn2bzZfJR08KD5KOmDD6BHDz1KupEjF4/w2pLXmLZzGgABPgH0vqc3A+oNICRAA4jEM7hckFm+fLnVJYhINjAM+Pxz6NXLfJRUooT5KKlWLasrcz4Xr1zkvd/e46N1H5GclowNG+2qtePdRu8SHqLJDeJZXC7IiIj7iYuDzp3hu+/M7RYtYOJEyJ/f2rqcTXJaMuM2jOPtlW9zPvE8APdH3M/IB0ZSo2gNi6sTsYaCjIhYats2aNUK9u8HHx8YPhx699ajpOsZhsGPu39k4OKBHLxwEIBKhSrxwQMf8HCZhzWQVzyagoyIWMIwYMIEc/xLUhKEh5t3ZOrUsboy57Lm+Bpe/fVV1vy5BoDQXKG80+gdnqvxHD5e+hUuov8XiEiOi4+Hl16CqVPN7WbNYNIkKKBmyw4Hzx9k0JJB/LDLnLoV5BtE3zp96VevH7n9cltcnYjzUJARkRy1Y4f5KGnvXvD2hmHDzLVivFyi81v2O5dwjndXvsvYDWNJsadgw8bzNZ7n7UZvE5YnzOryRJyOgoyI5AjDMNeC6d4drlyBO+4wHyXVq2d1Zc7hSuoVPl3/KUN/G8rFKxcBeKjMQ4xoMoKqoVWtLU7EiSnIiEi2u3wZunaFyZPN7Ycegm+/hUKFrK3LGRiGwXd/fMegJYM4cvEIAJGhkXzwwAc8WPpBa4sTcQEKMiKSrf74w3yUtHu3+fjo3XdhwAA9SgJYeXQlfX/ty4aTGwAIyxPG0PuH0i6yHd5easEikhEKMiKSbb75Bl5+GRIToWhRmD4dGjSwuirr7f1rLwOXDGTWnlkA5PbLzYB6A+hTpw9BvkHWFifiYhRkRCTLJSSYHaq/+cbcfuABiI6GwoUtLctyZy+f5a0Vb/H5xs9JM9Lwtnnz4p0v8mbDNwnNHWp1eSIuSUFGRLLU7t3mo6Q//jAfH731Frz2mmc/SkpMSWTM2jEM+30Yl5IvAfBouUcZ3mQ4lQpVsrg6EdemICMiWWbyZOjSxbwjU6SIuU5Mo0ZWV2Udu2FnyvYpvL70dY7HHQfgzqJ3MvKBkTSK8OAfjEgWUpARkduWmAivvAJffWVuN24MU6ZAqAc/LVl6eCl9f+3LlpgtABQPKc7Q+4fybNVn8bJ58O0pkSymICMit2XvXvNR0o4dZn+kN96A//3PXOzOE+06u4v+i/ozb/88AIL9g3mt/mv0qN2DQN9Ai6sTcT8KMiKSaVOnml2rL182B/JOnWrejfFEMfExvLHsDb7c8iV2w46Plw9da3ZlcIPBFMqlBXNEsouCjIj8Z8eOQc+eMGuWud2woRliiha1siprXE6+zKg1oxi+ajiXUy4D8HiFx3m/yfuUK1DO4upE3J+CjIhkWEoKjB5tzkRKSDAfH73+OgwZ4nmPkq6kXmHS1km8vfJtTl46CUCtO2rx4YMfUr94fYurE/EcCjIikiErV5ptBnbtMrfr14dx46BKFWvrymkXr1zk842f89G6j4iJjwEgIm8EwxoP4+nKT2Oz2SyuUMSzKMiIyC2dOQP9+pm9kQAKFoQPPoAOHczBvZ7iRNwJxqwdwxebvnCsBRMeHM6rdV6lS80u+Pv4W1yhiGdSkBGRG0pLg/HjzcXsLl40Q0vnzvDee5A/v9XV5ZxdZ3fxweoPmLJ9Cin2FACqFK5C/7r9aV2lNb7evhZXKOLZFGRE5B82bTIfI20wexlSowZ89hncc4+1deWkVcdWMXzVcH7e97NjX4MSDRhQbwAPl3lYj5BEnISCjIg4XLwIgwebocVuh+Bgs1t1167g4wG/LeyGnbn75jJ81XBWH18NgA0bj1d8nH51+3FPMQ9KciIuwgN+NYnIvzEMc/r0q6/C6dPmvjZt4MMPPWNKdXJaMlO2T+GD1R+w+6/dAPh5+9GhWgderfMq5QuWt7hCEbkZBRkRD7dnD7z8MixbZm6XK2fekfGEhe3ikuIYv2k8o9eOdkyhDvYP5uWaL9Ojdg+K5vGAFCfi4hRkRDxUQgIMHWrOQEpJgYAAs7VA377g7+YTcGLiY/ho7UeM2ziO2KRYAMLyhNH7nt50vqszwf7BFlcoIhmlICPigX7+2WzyePSouf3II/DJJ1CqlLV1Zbd95/YxcvVIJm2bRHJaMgAVClagf93+PFv1WU2hFnFBCjIiHuToUejRA+bMMbfDw+Hjj+Gxx9x7TZh1f65jxOoRzNw9EwMDgLrhdRlQbwCPlntU3ahFXJiCjIgHSE6GUaPg7bchMdGcgdSnj9laIFcuq6vLHoZh8MuBXxixagQrjq5w7G9erjkD6g2gXvF6FlYnIllFQUbEzS1fbg7m3W1OxqFBA3Mwb+XKlpaVbVLSUpi+czojVo9g55mdAPh6+dI2si396vajUqFKFlcoIllJQUbETZ0+bQ7cjY42twsVgpEjoV0793yMFJ8cz5ebv2TUmlEcjzsOQG6/3Lx010v0uqcXxYKLWVyhiGQHBRkRN5OWBl98YbYWiI01Q8tLL5mtBfLls7q6rHfm8hk+WfcJYzeM5cKVCwCE5gqlZ+2edL27K3kD8lpboIhkKwUZETeycSN06WK2GAC4806zQ3WtWtbWlR0Onj/Ih2s+ZOLWiVxJvQJA2fxl6Vu3L+2rtSfAJ8DiCkUkJyjIiLiBixfh9dfN0GIYZmuB994zQ423t9XVZa1NJzcxYvUIZuyagd2wA3B32N0MqDeAlhVa4u3lZhcsIrekICPiwgwDpkwxWwucOWPua9vWHAtTpIi1tWUlwzBYdGgRI1aNYMnhJY79D5d5mP71+nNfifvUxFHEQynIiLioXbvM2Ugr/n9mcYUK5mykRo2srSsrpdpT+eGPHxixegRbY7YC4G3zpk3VNvSr24/I0EhrCxQRyynIiLiYy5fNjtQjR0JqKgQGmh2rX30V/Pysri5rJKQk8PWWr/lwzYccuXgEgCDfIF6880V639ObEnlLWFugiDgNBRkRFzJ7trky77Fj5vajj5or80ZEWFtXVjmXcI5P13/KJ+s/4VziOQAKBhWkR60evHz3yxQIKmBxhSLibBRkRFzAkSNmgPn5Z3O7ePFrrQXcwZGLRxi1ZhRfbfmKhJQEACLyRtC3bl86Vu9IkG+QxRWKiLNSkBFxYsnJ5iOkd9+91lqgb1+zS7U7tBbYFrONEatH8N3O70gz0gCoUaQGA+oN4MlKT+LjpV9RInJr+i0h4qSWLoVu3WDPHnO7YUMYOxYqufgK+4ZhsPzIcoavGs7Cgwsd+x8o9QD96/WncURjzUASkQxTkBFxMjEx5l2XKVPM7cKF4cMPzWnVrvz3Pc2exsw9MxmxagQbTm4AwMvmxdOVn6Zf3X7cWfROiysUEVekICPiJNLSzAXtXn8d4uLM0NK1KwwdCnnzWl1d5iWmJDJp2yRGrh7JwQsHAQjwCaBTjU70qdOHUvlKWVyhiLgyBRkRJ7B+vRlaNm82t++6Cz7/HGrWtLau23Eh8QKfbfiMj9d/zJnL5mp9+QPz0/3u7nSv1Z1CuQpZXKGIuAMFGRELXbhgNnf84gtzld6QELO1wEsvuW5rgeOxxxmzdgzjN48nPjkegOIhxXm1zqt0qtGJXH5uMEpZRJyGUweZYcOG8dNPP7Fnzx4CAwOpW7cuw4cPp3z58laXJnJbDAMmTzbHwpw9a+5r1w4++ABCQ62tLbP+OPMHH6z+gCk7ppBqTwUgMjSS/nX783Tlp/H19rW4QhFxR04dZFasWEG3bt24++67SU1N5bXXXuPBBx9k165d5HKHuafikf74w2wtsHKluV2xotlaoGFDS8vKFMMw+P3Y74xYPYK5++Y69jcq2Yj+9frTtHRTzUASkWxlMwzDsLqIjDp79iyFCxdmxYoVNGjQIENfExcXR0hICLGxsQQHB2dzhSI3Fx8P77wDo0Zday3wxhvQu7frtRa4kHiBGbtm8PXWr1n751oAbNh4ouIT9K/Xn1p31LK4QhFxdRn9++3Ud2T+LjY2FoD8+fPf9JykpCSSkpIc23Fxcdlel8itGAbMmgU9e8Lx4+a+xx6Djz6CEi7UMigpNYn5++cTvSOaufvmkpyWDIC/tz8dqnWgb92+lC1Q1uIqRcTTuEyQsdvt9OrVi3r16lGlSpWbnjds2DDeeuutHKxM5OYOHTJbC8ybZ26XKAGffALNm1tbV0bZDTurjq0iens03+/6notXLjqOVSlchXaR7WhfrT1FchexrkgR8Wgu82ipa9eu/PLLL/z+++8UK1bspufd6I5MeHi4Hi1JjkpKMgfuDh0KV66Ar++11gJBLtA2aPfZ3URvj2bKjikcjT3q2B+WJ4y2VdsSFRlFZGikhRWKiLtzq0dL3bt3Z+7cuaxcufKWIQbA398ff3//HKpM5J+WLDEH8+7bZ243amS2FqhY0dq6/s2pS6eYvnM60Tui2Xxqs2N/Hr88PFXpKaIio7ivxH14e7novHARcUtOHWQMw+CVV15h5syZLF++nIiICKtLErmpU6fg1Vdh2jRzOzTUHNjbpo3zthaIT45n5u6ZRO+IZvGhxdgNOwA+Xj48XOZhoiKjaF6uOYG+gRZXKiJyY04dZLp168bUqVOZPXs2efLkISYmBoCQkBACA/WLVZxDaqo5fXrwYLO1gJeXeUfmnXecs7VAqj2VxYcWE709mpl7ZpKQkuA4VqdYHaIio3i68tMUDCpoYZUiIhnj1GNkbrb+xMSJE+nYsWOGXkPTryU7rVtnthbYssXcvvtus1/SXXdZW9ffGYbBplObiN4ezbSd0xwtAwDK5C9Du8h2PFv1WcrkL2NhlSIi17jFGBknzljiwQwDVqww78LMmGFu580Lw4bBiy86V2uBwxcOM2XHFKK3R7P33F7H/oJBBWlduTVRkVHUuqOWFq0TEZfl1EFGxJlcuACTJpnNHPdeywS0bw8jRjhPa4FzCef4YdcPRG+PZtXxVY79AT4BtKzQkqiqUTxY+kG1DBARt6AgI3ILhmF2pv78c5g+3ZxKDZArF0RFQZcuUL26pSUCcCX1CnP3zSV6ezTz988nxZ4CmKvtNi7VmKiqUTxe8XGC/fV4VUTci4KMyA3Ex8PUqWaAuTr+BSAy0hwT8+yzYPWQK7thZ+XRlURvj2bGrhnEJsU6jlUvUp2oqlG0rtKaO4LvsLBKEZHspSAjcp3t283wEh0Nly6Z+/z94ZlnzLsv99xj/VTqnWd2Er09mqk7pnI87rhjf3hwOG2rtqVtZFuqFL756tciIu5EQUY83pUr8MMPZoBZvfra/rJlzfDSoQMUKGBdfQAn4k4wbec0ordHs+30Nsf+EP8QWlVqRVRkFPeWuBcvm5eFVYqI5DwFGfFY+/fDF1/AxIlw/ry5z8cHWrY0Hx81amTt3Ze4pDjHYnVLDi3BwJzF5+vlS7NyzYiqGkWzcs0I8AmwrkgREYspyIhHSUmBOXPMtV6WLLm2v3hx6NwZnn8eiha1sL60FH49+CvRO6KZvWc2iamJjmP1i9cnqmoUrSq3In/gzTvAi4h4EgUZ8QjHjsGECfDll/D/C0Rjs8Ejj5iPjx5+2Lr1XwzDYP2J9URvj2b6H9P5K+Evx7HyBcoTFRnFs1WfpVS+UtYUKCLixBRkxG2lpcHChebYl3nzwG62ESI0FDp1Mu/AlChhXX0Hzh9gyvYpRO+I5sD5A479hXMVpk2VNkRFRnFX0bu0WJ2IyC0oyIjbOX0avv4axo+HI0eu7W/UyBz78thj4OdnTW1nL5/l+z++J3pHNGv/XOvYH+QbxOMVHicqMoompZrg46X/a4qIZIR+W4pbuNo2YNw4mDnTHAsDkC8fdOxo3n2pUMGa2hJTEpmzdw7RO6JZcGABqfZUALxsXjxQ6gGiIqNoWaEluf1yW1OgiIgLU5ARl3aztgH33GOOfXn6abCiUXqaPY3lR5YTvSOaH3f9yKXkS45jdxW9i6hIc7G6IrmL5HxxIiJuREFGXI6ztg0wDIPtp7ebi9XtnMrJSycdx0qElCAqMoq2VdtSsVDFnC9ORMRNKciIy3DWtgHHY48zdcdUondEs/PMTsf+fAH5eLry00RFRlE3vK4WqxMRyQYKMuL0nK1tgGEY7P5rN4sPLWbmnpmsOLLCsVidn7cfzcs1JyoyiofLPIy/j3/OFSYi4oEUZMQpOVvbgD/j/mTxocUsObyEJYeWcCr+VLrjDUo0IKpqFE9Veop8gflyrjAREQ+nICNO5WZtAx5/3AwwOdU24ELiBZYfWe4IL3vP7U13PMAngHuL38sDpR7g6cpPUyKvhQvSiIh4MAUZsZwztA24knqF1cdXs/jQYhYfWsymU5uwG3bHcS+bFzXDatIkoglNSjWhTngd9TgSEXECCjJiGSvbBqTZ09gSs8Vxx+X3Y79zJfVKunMqFKxA44jGNCnVhIYlG5I3IG/2FCMiIpmmICM5yqq2AYZhcOD8AfOOy+HFLDu8jAtXLqQ7p2juojQp1YTGEY1pXKoxxYKLZX0hIiKSpRRkJEdY0TYgJj6GJYeWsOTwEhYfWszxuOPpjgf7B9OwZEPH46IKBSuor5GIiItRkJFsk9NtAy4lXWLF0RWOx0XXr+kC5tTouuF1aRLRhMalGlMzrKZ6GomIuDj9Fpcsl1NtA5LTkln35zrH46L1J9Y7+hgB2LBRo2gNxziX+sXrE+QbdPvfWEREnIaCjGSJm7UNyJ3bbBvw0ku33zbAbtjZcXqH447LyqMruZxyOd05pfOVdoxzaRTRiIJBBW/vm4qIiFNTkJHb8m9tA9q2hTx5Mv/6hy8cdoxxWXJ4CX8l/JXueKGgQjQu1djxuKhk3pKZ/2YiIuJyFGTkPzt92hz7smgRfPdd1rYN+CvhL5YeXsqSQ0tYfHgxhy4cSnc8l28u7it5n+NxUZXCVdTDSETEgynIyL86dw6WL4dly8yPXbvSH7+dtgGXky/z+7HfHXdctsRsSXfc2+bNPcXucTwuql2sNn7eWTy9SUREXJaCjPzDhQuwcuW14LJ9+z/PqVbNnDrdvPl/axuQak9lw4kNjsdFq4+vJsWeku6cKoWrOKZENyjRgDz+t/FsSkRE3JqCjBAXB7/9di24bNliDt69XuXKZmBp1Ajuuy/jd16u7xS95PASlh9ZTlxSXLpziocUd4xxuT/iforkLpJFVyYiIu5OQcYDXb4Mv/9+Lbhs2mSuuHu98uWvBZeGDaFw4Yy//p9xfzrGuNyoU3S+gHzcH3E/TUqZd11K5yuthehERCRTFGQ8QGIirF59LbisXw+pqenPKV06fXAJC8v461/tFH31cdHNOkVfHaBbvUh1vL2yqYmSiIh4FAUZN5SUBGvXXgsua9dCcnL6c0qUuBZcGjWC8PCMvXZcUhx7/trDrrO7+OPMH6w8tpKNJzeqU7SIiFhCQcYNJCebd1mWLTNnF61efW1BuqvuuCN9cImIuPVrnks4x66zu9h1dhe7/9rt+N8/4/684fnqFC0iIlZQkHFBqamwceO1Oy6rVkFCQvpzQkPTB5cyZf45s8gwDE7Fn2L32d3/CC1nE87e9PsXzV2USoUqUbFgRWqG1VSnaBERsYyCjAtISzNnEl0NLr/9Zq6oe71ChcyxLQ0bmsGlQoVrwcVu2Dly8ei1Oytnd7PrL/N/Y5Nib/p9S+YtScWCFR2hpVKhSlQsVFF3W0RExGkoyDghu91cu+VqcFm5EmL/ljfy5bsWWho1gkqVwE4qB88fZNfZXcz8/drjoD1/7SEhJeGG38vb5k3p/KXTh5WCFalQsAK5/HJl/8WKiIjcBgUZJ2AY8Mcf14LLihVw/nz6c4KDzfVbGjWCug2u4B+2j73nzLDy9u7d7Fq5i33n9v1jcbmr/Lz9KF+gPBULVaRSwUqOuytl85fF38c/B65SREQk6ynIWMAwYO/ea8Fl+XI4+7chKblzQ5374qlQfw8FKuwiIddu9pzbxbizu+k772C6WULXy+WbiwoFK6S7w1KpUCUi8kXg46W3W0RE3Iv+suUAw4CDB9MHl1PXrxEXcAG/Mrsofc9u8pfbRVr+3ZxM3sWiuGMsSgK2/fM18wbk/UdYqViwIuEh4WqiKCIiHkNBJpscOXItuCxbBn/+aUDu01BwNxTbhVfN3QSX2kVqvt3EE0MysBvADvx17XVCc4U6HgdVLHQttITmCtVquCIi4vEUZLLI8eNmYFm6zGDJhuP8mbTLDC2FdsGD//+/gRcc59uBi9d9fXhweLo7K1fHsOQPzJ/TlyIiIuIyFGQy6c8TaXy/6BALNu1m07FdnPf6/7ASthtaXb7h13jZvCiVr9Q/HgdVKFhBHZ5FREQyQUEmk2p90JpT+WZAQcyP6/jYfCmTvyxVQiuleyRUrkA5LdUvIiKShRRkMqlq0fLExAdSyGbOEKpfoSJ3FjNDS+l8pfH19rW6RBEREbdnMwzDsLqI7BQXF0dISAixsbEEBwdn2esmJCcS4OuvGUIiIiLZIKN/v13ir/DYsWMpWbIkAQEB1K5dm/Xr11tdEkF+gQoxIiIiFnP6v8Tfffcdffr04Y033mDz5s1Uq1aNpk2bcubMGatLExEREYs5/aOl2rVrc/fdd/Ppp58CYLfbCQ8P55VXXmHgwIH/OD8pKYmkpCTHdlxcHOHh4Vn+aElERESyj1s8WkpOTmbTpk00adLEsc/Ly4smTZqwZs2aG37NsGHDCAkJcXyEh4fnVLkiIiKSw5w6yPz111+kpaURGhqabn9oaCgxMTE3/JpBgwYRGxvr+Dh+/HhOlCoiIiIWcLvp1/7+/vj7q5uziIiIJ3DqOzIFCxbE29ub06dPp9t/+vRpihQpYlFVIiIi4iycOsj4+flx1113sWTJEsc+u93OkiVLqFOnjoWViYiIiDNw+kdLffr0oUOHDtSsWZNatWoxZswYLl++zHPPPWd1aSIiImIxpw8yzzzzDGfPnmXIkCHExMRQvXp1FixY8I8BwCIiIuJ5nH4dmduVXS0KREREJPu4xToyIiIiIreiICMiIiIuS0FGREREXJbTD/a9XVeHAMXFxVlciYiIiGTU1b/b/zaU1+2DzKVLlwDUc0lERMQFXbp0iZCQkJsed/tZS3a7nZMnT5InTx5sNluWvObVjtrHjx/3uJlQunZdu67dM3jqdYOu3Vmu3TAMLl26RFhYGF5eNx8J4/Z3ZLy8vChWrFi2vHZwcLDlb7RVdO26dk/jqdfuqdcNunZnuPZb3Ym5SoN9RURExGUpyIiIiIjLUpDJBH9/f9544w38/f2tLiXH6dp17Z7GU6/dU68bdO2udu1uP9hXRERE3JfuyIiIiIjLUpARERERl6UgIyIiIi5LQUZERERclscGmZUrV9K8eXPCwsKw2WzMmjUr3XHDMBgyZAhFixYlMDCQJk2asH///nTnnD9/nrZt2xIcHEzevHnp1KkT8fHx6c7Zvn079957LwEBAYSHhzNixIjsvrRbGjZsGHfffTd58uShcOHCtGzZkr1796Y758qVK3Tr1o0CBQqQO3dunnzySU6fPp3unGPHjtGsWTOCgoIoXLgw/fr1IzU1Nd05y5cv584778Tf358yZcrwzTffZPfl3dK4ceOIjIx0LPRUp04dfvnlF8dxd73uG3n//fex2Wz06tXLsc9dr//NN9/EZrOl+6hQoYLjuLte91UnTpwgKiqKAgUKEBgYSNWqVdm4caPjuLv+ritZsuQ/3nebzUa3bt0A937f09LSGDx4MBEREQQGBlK6dGneeeeddD2L3Op9NzzU/Pnzjddff9346aefDMCYOXNmuuPvv/++ERISYsyaNcvYtm2b0aJFCyMiIsJITEx0nPPQQw8Z1apVM9auXWv89ttvRpkyZYw2bdo4jsfGxhqhoaFG27ZtjZ07dxrTpk0zAgMDjS+++CKnLvMfmjZtakycONHYuXOnsXXrVuORRx4xihcvbsTHxzvO6dKlixEeHm4sWbLE2Lhxo3HPPfcYdevWdRxPTU01qlSpYjRp0sTYsmWLMX/+fKNgwYLGoEGDHOccOnTICAoKMvr06WPs2rXL+OSTTwxvb29jwYIFOXq915szZ44xb948Y9++fcbevXuN1157zfD19TV27txpGIb7XvffrV+/3ihZsqQRGRlp9OzZ07HfXa//jTfeMCpXrmycOnXK8XH27FnHcXe9bsMwjPPnzxslSpQwOnbsaKxbt844dOiQsXDhQuPAgQOOc9z1d92ZM2fSveeLFi0yAGPZsmWGYbj3+z506FCjQIECxty5c43Dhw8bP/zwg5E7d27jo48+cpzjTu+7xwaZ6/09yNjtdqNIkSLGBx984Nh38eJFw9/f35g2bZphGIaxa9cuAzA2bNjgOOeXX34xbDabceLECcMwDOOzzz4z8uXLZyQlJTnOGTBggFG+fPlsvqKMO3PmjAEYK1asMAzDvE5fX1/jhx9+cJyze/duAzDWrFljGIYZAr28vIyYmBjHOePGjTOCg4Md19q/f3+jcuXK6b7XM888YzRt2jS7L+k/yZcvn/Hll196zHVfunTJKFu2rLFo0SLjvvvucwQZd77+N954w6hWrdoNj7nzdRuG+fumfv36Nz3uSb/revbsaZQuXdqw2+1u/743a9bMeP7559Pte+KJJ4y2bdsahuF+77vHPlq6lcOHDxMTE0OTJk0c+0JCQqhduzZr1qwBYM2aNeTNm5eaNWs6zmnSpAleXl6sW7fOcU6DBg3w8/NznNO0aVP27t3LhQsXcuhqbi02NhaA/PnzA7Bp0yZSUlLSXXuFChUoXrx4umuvWrUqoaGhjnOaNm1KXFwcf/zxh+Oc61/j6jlXX8NqaWlpTJ8+ncuXL1OnTh2Pue5u3brRrFmzf9To7te/f/9+wsLCKFWqFG3btuXYsWOA+1/3nDlzqFmzJq1ataJw4cLUqFGDCRMmOI57yu+65ORkoqOjef7557HZbG7/vtetW5clS5awb98+ALZt28bvv//Oww8/DLjf+64gcwMxMTEA6f4BX92+eiwmJobChQunO+7j40P+/PnTnXOj17j+e1jJbrfTq1cv6tWrR5UqVQCzLj8/P/LmzZvu3L9f+79d183OiYuLIzExMTsuJ0N27NhB7ty58ff3p0uXLsycOZNKlSq5/XUDTJ8+nc2bNzNs2LB/HHPn669duzbffPMNCxYsYNy4cRw+fJh7772XS5cuufV1Axw6dIhx48ZRtmxZFi5cSNeuXenRoweTJk0CPOd33axZs7h48SIdO3YE3PvfO8DAgQNp3bo1FSpUwNfXlxo1atCrVy/atm0LuN/77vbdr+XmunXrxs6dO/n999+tLiXHlC9fnq1btxIbG8uMGTPo0KEDK1assLqsbHf8+HF69uzJokWLCAgIsLqcHHX1v0IBIiMjqV27NiVKlOD7778nMDDQwsqyn91up2bNmrz33nsA1KhRg507d/L555/ToUMHi6vLOV999RUPP/wwYWFhVpeSI77//numTJnC1KlTqVy5Mlu3bqVXr16EhYW55fuuOzI3UKRIEYB/jGA/ffq041iRIkU4c+ZMuuOpqamcP38+3Tk3eo3rv4dVunfvzty5c1m2bBnFihVz7C9SpAjJyclcvHgx3fl/v/Z/u66bnRMcHGzpHw8/Pz/KlCnDXXfdxbBhw6hWrRofffSR21/3pk2bOHPmDHfeeSc+Pj74+PiwYsUKPv74Y3x8fAgNDXXr679e3rx5KVeuHAcOHHD7971o0aJUqlQp3b6KFSs6Hq15wu+6o0ePsnjxYl544QXHPnd/3/v16+e4K1O1alXatWtH7969HXdj3e19V5C5gYiICIoUKcKSJUsc++Li4li3bh116tQBoE6dOly8eJFNmzY5zlm6dCl2u53atWs7zlm5ciUpKSmOcxYtWkT58uXJly9fDl1NeoZh0L17d2bOnMnSpUuJiIhId/yuu+7C19c33bXv3buXY8eOpbv2HTt2pPtHvmjRIoKDgx2/NOvUqZPuNa6ec/U1nIXdbicpKcntr7tx48bs2LGDrVu3Oj5q1qxJ27ZtHZ+78/VfLz4+noMHD1K0aFG3f9/r1av3j+UV9u3bR4kSJQD3/l131cSJEylcuDDNmjVz7HP39z0hIQEvr/R/3r29vbHb7YAbvu85OrTYiVy6dMnYsmWLsWXLFgMwRo0aZWzZssU4evSoYRjm1LS8efMas2fPNrZv32489thjN5yaVqNGDWPdunXG77//bpQtWzbd1LSLFy8aoaGhRrt27YydO3ca06dPN4KCgiydkti1a1cjJCTEWL58ebqpiQkJCY5zunTpYhQvXtxYunSpsXHjRqNOnTpGnTp1HMevTkt88MEHja1btxoLFiwwChUqdMNpif369TN2795tjB071vJpiQMHDjRWrFhhHD582Ni+fbsxcOBAw2azGb/++qthGO573Tdz/awlw3Df63/11VeN5cuXG4cPHzZWrVplNGnSxChYsKBx5swZwzDc97oNw5xq7+PjYwwdOtTYv3+/MWXKFCMoKMiIjo52nOOuv+sMwzDS0tKM4sWLGwMGDPjHMXd+3zt06GDccccdjunXP/30k1GwYEGjf//+jnPc6X332CCzbNkyA/jHR4cOHQzDMKenDR482AgNDTX8/f2Nxo0bG3v37k33GufOnTPatGlj5M6d2wgODjaee+4549KlS+nO2bZtm1G/fn3D39/fuOOOO4z3338/py7xhm50zYAxceJExzmJiYnGyy+/bOTLl88ICgoyHn/8cePUqVPpXufIkSPGww8/bAQGBhoFCxY0Xn31VSMlJSXdOcuWLTOqV69u+Pn5GaVKlUr3Pazw/PPPGyVKlDD8/PyMQoUKGY0bN3aEGMNw3+u+mb8HGXe9/meeecYoWrSo4efnZ9xxxx3GM888k24dFXe97qt+/vlno0qVKoa/v79RoUIFY/z48emOu+vvOsMwjIULFxrAP67HMNz7fY+LizN69uxpFC9e3AgICDBKlSplvP766+mmSbvT+24zjOuW+hMRERFxIRojIyIiIi5LQUZERERcloKMiIiIuCwFGREREXFZCjIiIiLishRkRERExGUpyIiIiIjLUpARERERl6UgIyIup2HDhvTq1cvqMkTECSjIiIiIiMtSkBERERGXpSAjIi5v3rx5hISEMGXKFKtLEZEc5mN1ASIit2Pq1Kl06dKFqVOn8uijj1pdjojkMN2RERGXNXbsWF5++WV+/vlnhRgRD6U7MiLikmbMmMGZM2dYtWoVd999t9XliIhFdEdGRFxSjRo1KFSoEF9//TWGYVhdjohYREFGRFxS6dKlWbZsGbNnz+aVV16xuhwRsYgeLYmIyypXrhzLli2jYcOG+Pj4MGbMGKtLEpEcpiAjIi6tfPnyLF26lIYNG+Lt7c2HH35odUkikoNshh4ui4iIiIvSGBkRERFxWQoyIiIi4rIUZERERMRlKciIiIiIy1KQEREREZelICMiIiIuS0FGREREXJaCjIiIiLgsBRkRERFxWQoyIiIi4rIUZERERMRl/R9CLH0v+jfO5wAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "forward:\n",
      "        k       bf16       fp8\n",
      "0  1024.0   0.194941  0.188405\n",
      "1  2048.0   0.786695  0.567100\n",
      "2  3072.0   1.736629  1.182998\n",
      "3  4096.0   3.160715  2.021425\n",
      "4  5120.0   4.916526  3.100666\n",
      "5  6144.0   7.006277  4.391098\n",
      "6  7168.0   9.500768  5.980333\n",
      "7  8192.0  12.351304  7.658754\n"
     ]
    }
   ],
   "source": [
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['k'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[1024 * i for i in range(1, 8+1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['bf16', 'fp8'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"bf16\",\n",
    "            \"fp8\",\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"forward\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={'m':4096, 'bs':4, 'factor':4},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(bs, m, k, factor, provider):\n",
    "    n = k * factor\n",
    "    device = 'cuda'\n",
    "    dtype = torch.bfloat16\n",
    "    x = torch.randn(bs*m, k, dtype=dtype, device=device) \n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    if provider == 'bf16':\n",
    "        fc = torch.nn.Linear(k,n, bias=False, dtype=dtype, device=device)\n",
    "        ms = triton.testing.do_bench(lambda: fc(x))\n",
    "    if provider == 'fp8':\n",
    "        fc = DeepLinear(k,n, bias=False, dtype=dtype, device=device)\n",
    "        ms = triton.testing.do_bench(lambda: fc(x))\n",
    "    return ms\n",
    "benchmark.run(show_plots=True, print_data=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Backward"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/sharedata/mdy/miniforge/envs/cuda128/lib/python3.10/site-packages/torch/autograd/graph.py:823: UserWarning: Attempting to run cuBLAS, but there was no current CUDA context! Attempting to set the primary context... (Triggered internally at /pytorch/aten/src/ATen/cuda/CublasHandlePool.cpp:180.)\n",
      "  return Variable._execution_engine.run_backward(  # Calls into the C++ engine to run the backward pass\n"
     ]
    },
    {
     "data": {
      "image/png": "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ixYZISEhxtChQx3nuNP7nm2DzLp16wzghkfXrl0NwzCHp40aNcooUKCA4evrazRq1MjYt29futc4d+6c0bFjRyNnzpxGUFCQ8eSTTxrx8fHpzvn111+NOnXqGL6+vsY999xjjB8/Pqsu8aZuds2AMWvWLMc5V65cMfr27Wvkzp3bCAgIMB577DHj5MmT6V7n999/N5o3b274+/sbISEhxpAhQ4yUlJR056xbt86oVKmS4ePjY9x7773pvocVunfvbhQpUsTw8fEx8uXLZzRq1MgRYgzDfa/7Vv4eZNz1+tu3b28ULFjQ8PHxMe655x6jffv26eZRcdfrvmrZsmVG+fLlDV9fX6NMmTLGjBkz0h131791hmEY3377rQHccD2G4d7ve1xcnDFw4ECjcOHChp+fn3HvvfcaI0eOTDdM2p3ed5thXDfVn4iIiIgLUR8ZERERcVkKMiIiIuKyFGRERETEZSnIiIiIiMtSkBERERGXpSAjIiIiLktBRkRERFyWgoyIiIi4LAUZEXE59evXZ9CgQVaXISJOQEFGREREXJaCjIiIiLgsBRkRcXlff/01wcHBfPrpp1aXIiJZzMvqAkRE/ot58+bx9NNPM2/ePP7v//7P6nJEJIupRUZEXNa0adPo27cvy5YtU4gRyabUIiMiLmnhwoWcPn2an376iWrVqlldjohYRC0yIuKSKleuTL58+fjoo48wDMPqckTEIgoyIuKSihcvzrp16/jqq68YMGCA1eWIiEV0a0lEXFapUqVYt24d9evXx8vLi8mTJ1tdkohkMQUZEXFppUuXZu3atdSvXx9PT08mTJhgdUkikoVshm4ui4iIiItSHxkRERFxWQoyIiIi4rIUZERERMRlKciIiIiIy1KQEREREZelICMiIiIuS0FGREREXJaCjIiIiLgsBRkRERFxWQoyIiIi4rIUZERERMRl/T+3NrUiSPpyqgAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "backward:\n",
      "        k       bf16        fp8\n",
      "0  1024.0   0.346827   0.344999\n",
      "1  2048.0   1.475767   1.101843\n",
      "2  3072.0   3.385527   2.342549\n",
      "3  4096.0   6.052966   3.950739\n",
      "4  5120.0   9.549991   6.126831\n",
      "5  6144.0  14.244321   8.698150\n",
      "6  7168.0  19.183290  11.872935\n",
      "7  8192.0  25.788393  15.218900\n"
     ]
    }
   ],
   "source": [
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['k'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[1024 * i for i in range(1, 8+1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['bf16', 'fp8'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"bf16\",\n",
    "            \"fp8\",\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"backward\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={'m':4096, 'bs':4, 'factor':4},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(bs, m, k, factor, provider):\n",
    "    n = k * factor\n",
    "    device = 'cuda'\n",
    "    dtype = torch.bfloat16\n",
    "    x = torch.randn(bs*m, k, dtype=dtype, device=device)\n",
    "    x.requires_grad_(True)\n",
    "    \n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    if provider == 'bf16':\n",
    "        fc = torch.nn.Linear(k,n, bias=False, dtype=dtype, device=device)\n",
    "        y = fc(x)\n",
    "        dy = torch.rand_like(y)\n",
    "        ms = triton.testing.do_bench(lambda: y.backward(dy, retain_graph=True), grad_to_none=[x])\n",
    "    if provider == 'fp8':\n",
    "        fc = DeepLinear(k,n, bias=False, dtype=dtype, device=device)\n",
    "        y = fc(x)\n",
    "        dy = torch.rand_like(y)\n",
    "        ms = triton.testing.do_bench(lambda: y.backward(dy, retain_graph=True), grad_to_none=[x])\n",
    "    return ms\n",
    "benchmark.run(show_plots=True, print_data=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
